﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Krilib.Control.Surface;
using Krilib.Data;
using Krile.Data;

namespace Krile.Forms.MainForm.Controls.Surface
{
    /// <summary>
    /// Surface core control
    /// </summary>
    internal partial class SurfaceCore : Control
    {
        SurfaceRefreshManager refreshman;
        SurfaceMouseManager mouseman;
        public SurfaceRefreshManager Refresher { get { return refreshman; } }

        SurfaceRenderer renderer;

        /// <summary>
        /// VerticalPositionが変更された
        /// </summary>
        internal event Action OnVerticalPositionChanged;

        /// <summary>
        /// 選択中のアイテムが変更された
        /// </summary>
        internal event Action OnSelectedItemChanged;

        /// <summary>
        /// ステータスの全体が変化した
        /// </summary>
        internal event Action OnStatusesChanged;

        /// <summary>
        /// Statusesのロックオブジェクト。
        /// <para>このオブジェクトを使ってのlockは、Statusesを列挙したい時以外に利用「しない」こと！</para>
        /// </summary>
        internal object statusesLocker = new object();
        private XStatus[] statuses = null;
        /// <summary>
        /// 表示するメッセージオブジェクト一覧
        /// </summary>
        internal XStatus[] Statuses
        {
            get { return statuses; }
            set
            {
                bool sus = Refresher.IsSuspending;
                if (!sus)
                    Refresher.Suspend();
                try
                {
                    if (Core.Config == null || Core.Config.Timeline.ListBottomNewer)
                    {
                        statuses = value;
                    }
                    else
                    {
                        ulong puid = 0;
                        long vp = 0;
                        if (statuses == null || value == null || Core.Config == null || SelectedIndex == -1)
                            SelectedIndex = -1;
                        else
                        {
                            //選択ステータス描画開始位置の、画面上端からの位置を算出
                            puid = SelectedStatus.BaseStatus.UID;
                            vp = SelectedIndex * Core.Config.Appearance.Renderer.Detail.NormalListHeight;
                            vp -= VerticalPosition;
                        }
                        lock (statusesLocker)
                        {
                            statuses = value;
                        }
                        if (puid > 0)
                            SelectStatusByUID(puid, false);
                        if (SelectedIndex != -1)
                        {
                            //画面上端からの開始位置
                            //絶対開始位置 - 画面上端の開始位置 = スクロールオフセット
                            long ofs = SelectedIndex * Core.Config.Appearance.Renderer.Detail.NormalListHeight - vp;
                            if (ofs > ListActualHeight - this.Height)
                                ofs = ListActualHeight - this.Height;
                            if (ofs < 0)
                                ofs = 0;
                            this.VerticalPosition = (int)ofs;
                        }
                    }
                    Refresher.SetUpdateComposition();
                    if (OnStatusesChanged != null)
                        OnStatusesChanged.Invoke();
                }
                finally
                {
                    if (!sus)
                        Refresher.Restore(true);
                    else
                        Refresher.RefreshSurface(true);
                }
            }
        }

        private long _selectedIndex = -1;
        /// <summary>
        /// 選択されているメッセージオブジェクトのインデックス
        /// </summary>
        internal long SelectedIndex
        {
            get { return _selectedIndex; }
            set
            {
                bool sus = Refresher.IsSuspending;
                try
                {
                    if (!sus)
                        Refresher.Suspend();
                    //リソースの破棄
                    releaseSelectedItemResource();
                    _selectedIndex = value;
                    Refresher.SetUpdateComposition();
                    if (OnSelectedItemChanged != null)
                        OnSelectedItemChanged.Invoke();
                }
                finally
                {
                    if (!sus)
                        Refresher.Restore(true);
                    else
                        Refresher.RefreshSurface(true);
                }
            }
        }

        /// <summary>
        /// 選択中のステータス
        /// </summary>
        internal XStatus SelectedStatus
        {
            get
            {
                if (SelectedIndex >= 0)
                    return statuses[SelectedIndex];
                else
                    return null;
            }
        }

        /// <summary>
        /// リスト内部高さ
        /// </summary>
        internal int ListActualHeight
        {
            get
            {
                if (statuses != null && SelectedIndex >= 0)
                    return renderer.NormalItemHeight * (statuses.Length - 1) + renderer.SelectedItemHeight;
                else
                    if (statuses == null || statuses.Length == 0)
                        return 0;
                    else
                        return renderer.NormalItemHeight * statuses.Length;
            }
        }

        long verticalPosition = 0;
        /// <summary>
        /// Yスクロール位置を設定します。
        /// </summary>
        internal long VerticalPosition
        {
            get { return verticalPosition > ListActualHeight ? ListActualHeight : verticalPosition; }
            set
            {
                bool sus = Refresher.IsSuspending;
                try
                {
                    if (!sus)
                        Refresher.Suspend();
                    verticalPosition = value;
                    if (OnVerticalPositionChanged != null)
                        OnVerticalPositionChanged.Invoke();
                }
                finally
                {
                    if (!sus)
                        Refresher.Restore(false);
                    else
                        Refresher.RefreshSurface(false);
                }
            }
        }

        /// <summary>
        /// 選択を解除
        /// </summary>
        /// <param name="update">描画を更新するか</param>
        internal void Deselect(bool update)
        {
            if (update)
                SelectedIndex = -1;
            else
                _selectedIndex = -1;
        }

        //********************************* [ Supernatural Border ! ] *********************************

        /// <summary>
        /// コンストラクタ
        /// </summary>
        internal SurfaceCore()
        {
            refreshman = new SurfaceRefreshManager((r) => InvokeRefresh(r));
            renderer = new SurfaceRenderer(this);
            mouseman = new SurfaceMouseManager(this, renderer);

            InitializeComponent();
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.Opaque, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            SetStyle(ControlStyles.Selectable, true);
            SetStyle(ControlStyles.UserMouse, true);
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.ResizeRedraw, true);
        }

        protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
        {
            switch(e.KeyCode)
            {
                case Keys.Up:
                case Keys.Down:
                case Keys.Left:
                case Keys.Right:
                    e.IsInputKey = true;
                    return;
            }
            base.OnPreviewKeyDown(e);
        }

        object refLocker = new object();
        bool refreshing = false;
        bool refreshRequired = false;
        private void InvokeRefresh(bool recomposite)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Action<bool>(InvokeRefresh), recomposite);
            }
            else
            {
                lock (refLocker)
                {
                    if (refreshing)
                    {
                        refreshRequired = true;
                        return;
                    }
                    refreshing = true;
                }
                do
                {
                    refreshRequired = false;
                    if (recomposite)
                        renderer.UpdateComposite();
                    base.Refresh();
                    Application.DoEvents();
                } while (refreshRequired);
                refreshing = false;
            }
        }

        public override void Refresh()
        {
            Refresher.RefreshSurface(false);
        }

        internal void Refresh(bool recomposite)
        {
            Refresher.RefreshSurface(recomposite);
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            renderer.Draw(pe.Graphics, (int)verticalPosition);
        }

        private bool IsListHasItem
        {
            get
            {
                return statuses != null && statuses.Length != 0;
            }
        }

        private void releaseSelectedItemResource()
        {
            if (SelectedStatus != null && SelectedStatus.BaseStatus.text.MessageObjects != null)
                foreach (var msgobj in SelectedStatus.BaseStatus.text.MessageObjects)
                    msgobj.Release();
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            Refresher.RefreshSurfaceDelay();
            base.OnSizeChanged(e);
        }

        public void EnsureVisible()
        {
            if (SelectedIndex < 0) return;
            long vs = SelectedIndex * Core.Config.Appearance.Renderer.Detail.NormalListHeight;
            long evs = vs + renderer.SelectedItemHeight;
            long cp = this.verticalPosition;
            long mp = this.ListActualHeight - this.Height;
            //表示末尾位置よりスクロール位置+画面高さの方が小さい == (一部)見えてない
            if (evs > cp + this.Height)
                cp = evs - this.Height;
            //表示開始位置よりスクロール位置の方が大きい == 見えてない
            if (vs < cp)
                cp = vs;
            if (cp > mp)
                cp = mp;
            if (cp < 0)
                cp = 0;
            this.VerticalPosition = cp;
        }

        public bool SelectStatusByUID(ulong uid, bool evisible)
        {
            int target = -1;
            lock (statusesLocker)
            {
                for (int idx = 0; idx < statuses.Length; idx++)
                {
                    if (statuses[idx].BaseStatus.UID == uid)
                    {
                        target = idx;
                        break;
                    }
                }
            }
            if(target ==  -1)
                return false;

            var sus = Refresher.IsSuspending;
            if (!sus)
                Refresher.Suspend();
            try
            {
                this.SelectedIndex = target;
                if (evisible)
                    EnsureVisible();
            }
            finally
            {
                if (!sus)
                    Refresher.Restore(true);
                else
                    Refresher.RefreshSurface(true);
            }
            return true;
        }

        internal ContextMenuStrip CreateContextMenu()
        {
            if (SelectedStatus == null)
                return null;
            ContextMenuStrip ret = new ContextMenuStrip();
            if (SelectedStatus.BaseStatus.ParentAgent.Status.AutoCreateReplyOperationTriggers)
                ret.Items.Add(new ToolStripMenuItem(Lang.Renderer.Context_Reply, Properties.Resources.comment_add, new EventHandler(Ctx_SetReply)));
            if (SelectedStatus.BaseStatus.ParentAgent.Status.AutoCreateFavOperationTriggers)
            {
                var fav = new ToolStripMenuItem(Lang.Renderer.Context_SetFav, Properties.Resources.star, new EventHandler(Ctx_SetFav));
                if (SelectedStatus.BaseStatus.favored)
                    fav.Checked = true;
                ret.Items.Add(fav);
            }
            if (Core.FormManager.MainForm.CustomContextMenus != null)
            {
                foreach (var i in Core.FormManager.MainForm.CustomContextMenus)
                {
                    if (i.IsShowMenu(SelectedStatus.BaseStatus))
                        ret.Items.Add(i.GetMenuItem());
                }
            }
            ret.Items.Add(new ToolStripMenuItem(Lang.Renderer.Context_ExtractUser, Properties.Resources.searchuser, new EventHandler(Ctx_ExtractUser)));
            ret.Items.Add(new ToolStripSeparator());
            var copyTS = new ToolStripMenuItem(Lang.Renderer.Context_Copy);
            copyTS.DropDownItems.Add(new ToolStripMenuItem(Lang.Renderer.Context_C_Text, Properties.Resources.copy, new EventHandler(Ctx_CopyText)));
            copyTS.DropDownItems.Add(new ToolStripMenuItem(Lang.Renderer.Context_C_UserID, Properties.Resources.user, new EventHandler(Ctx_CopyUserID)));
            ret.Items.Add(copyTS);            
            var obTS = new ToolStripMenuItem(Lang.Renderer.Context_OpenBrowser);
            obTS.DropDownItems.Add(new ToolStripMenuItem(Lang.Renderer.Context_O_Status, Properties.Resources.comment, new EventHandler(Ctx_OpenWebStatus)));
            obTS.DropDownItems.Add(new ToolStripMenuItem(Lang.Renderer.Context_O_User, Properties.Resources.searchuser, new EventHandler(Ctx_OpenWebUser)));
            ret.Items.Add(obTS);
            ret.Items.Add(new ToolStripMenuItem(Lang.Renderer.Context_Delete, Properties.Resources.delete, new EventHandler(Ctx_DeleteStatus)));
            return ret;
        }

        private void Ctx_SetReply(object o, EventArgs e)
        {
            if (SelectedStatus != null)
                Core.FormManager.MainForm.InputControlManager.SetReplyTo(SelectedStatus.BaseStatus.UID);
        }

        private void Ctx_SetFav(object o, EventArgs e)
        {
            if (SelectedStatus != null)
            {
                Action act = new Action(() =>
                {
                    Status target = SelectedStatus.BaseStatus;
                    Core.FormManager.MainForm.RefreshTL(false);
                    Core.FormManager.MainForm.SetStatusText(Lang.StatusText.Favouring);
                    var or = target.ParentAgent.Status.SetFavorite(target.UID, !target.favored);
                    if (or.Succeeded)
                    {
                        target.favored = !target.favored;
                    }
                    else
                    {
                        Subsystem.Debugger.AddReport(new Krilib.Bridges.DebugReport("Fav operation was failed at " + target.UID.ToString()));
                        Core.FormManager.MainForm.SetStatusText(Lang.StatusText.FavError, 3);
                    }
                    Core.FormManager.MainForm.RefreshTL(true);
                    Core.FormManager.MainForm.SetStatusText(Lang.StatusText.Favored, 3);
                });
                act.BeginInvoke((iar) => ((Action)iar.AsyncState).EndInvoke(iar), act);
            }
        }

        
        private void Ctx_CopyText(object o, EventArgs e)
        {
            if (SelectedStatus != null)
            {
                Clipboard.SetText(SelectedStatus.BaseStatus.text.Original);
                Core.FormManager.MainForm.SetStatusText(Lang.StatusText.Copied, 3);
            }
        }

        private void Ctx_CopyUserID(object o, EventArgs e)
        {
            if (SelectedStatus != null)
            {
                Clipboard.SetText(SelectedStatus.BaseStatus.user.id);
                Core.FormManager.MainForm.SetStatusText(Lang.StatusText.Copied, 3);
            }
        }

        private void Ctx_ExtractUser(object o, EventArgs e)
        {
            if (SelectedStatus != null)
                Data.DefaultKeyAssigns.AddTabFromUser(SelectedStatus.BaseStatus, Keys.None);
        }

        private void Ctx_OpenWebStatus(object o, EventArgs e)
        {
            if (SelectedStatus != null)
                Subsystem.CommonOperation.CallBrowser(SelectedStatus.BaseStatus.statusPage);
        }

        private void Ctx_OpenWebUser(object o, EventArgs e)
        {
            if (SelectedStatus != null)
                Subsystem.CommonOperation.CallBrowser(SelectedStatus.BaseStatus.user.profileUri);
        }

        private void Ctx_DeleteStatus(object o, EventArgs e)
        {
            if (SelectedStatus != null)
                Core.StatusManager.RemoveStatus(new[] { SelectedStatus.BaseStatus.UID });
        }
    }
}
